// 引入配置模块
const config = require('../config');
// 引入事件模块
const EventEmitter = require('events').EventEmitter;
// 引入logger日志
const Logger = require('./Logger');
// 定义日志
const logger = new Logger('room');
// 引入protooServer
const protooServer = require('protoo-server');

// 定义room 类
class Room extends EventEmitter {

  // 创建room的工厂方法
  static async create ({ worker, roomId }) {
    logger.info('正在创建room,【workerId: %s, roomId: %s】', worker.pid, roomId);
    // 创建protooRoom 与之对应
    const protooRoom = new protooServer.Room();
    // 通过worker创建router
    const router = await worker.createRouter(config.mediasoup.routerOptions)
    logger.info('一个新路由被创建 【routerId:%s】', router.id);
    // 创建扬声器观察者，每隔500ms检测一次
    const activeSpeakerObserver = await router.createActiveSpeakerObserver({
      interval: 500
    });
    // 创建音量级别观察者，用于监听房间中生产者音量的级别
    const audioLevelObserver = await router.createAudioLevelObserver({
      maxEntries: 1,
      threshold: -80,
      interval: 1000
    });
    // 调用room构造函数
    return new Room({ roomId, protooRoom, router, activeSpeakerObserver, audioLevelObserver });
  }

  // 构造器
  constructor({ roomId, protooRoom, router, activeSpeakerObserver, audioLevelObserver }) {
    super();
    // 设置监听器的数量，为不限制
    this.setMaxListeners(Infinity);
    logger.info('room 构造器正在初始化变量...');
    // 定义room的成员变量
    this._roomId = roomId;
    // 定义房间是否关闭标识,默认未关闭
    this._closed = false;
    // 路由信息
    this._router = router;
    // protooRoom
    this._protooRoom = protooRoom;
    // 音量方面的监听
    this._activeSpeakerObserver = activeSpeakerObserver;
    this._audioLevelObserver = audioLevelObserver;

    // 监听事件
    this._protooRoom.on('close', () => {
      logger.error('protooRoom【roomId:】 正在关闭...', roomId);
    });

    // 绑定路由器监听
    this._router.on('workerclose', () => {
      logger.error('worker 【workerId:%s】 关闭, 所以router 【routerId:%s】会触发关闭事件', worker.pid, router.id);
    });

    // 绑定router观察者事件
    this._router.observer.on('close', () => {
      logger.error('router 【routerId:%s】关闭了', router.id);
    });

    // 当有新的mediasoup transport 被创建是会触发该事件
    this._router.observer.on('newtransport', (transport) => {
      logger.info('一个新的mediasoup transport 【transportId:%s】 被创建', transport.id);
    });
    // 当有新的rtpObserver(或其子类)在该router上被创建时触发该事件
    this._router.observer.on('newrtpobserver', (rtpObserver) => {
      logger.info('一个新的mediasoup rtpObsever 【rptObserverId:%s】 被创建', rtpObserver.id);
    });


    // 监听路由器的关闭事件
    this._activeSpeakerObserver.on('routerclose', () => {
      logger.error('activeSpeakerObserver 【activeSpeakerObserverId:%s】 监听到 router 【routerId:%s】关闭了,将触发activeSpeakerObserver的关闭事件', activeSpeakerObserver.id, router.id);
    });
    // activeSpeakerObserver 的关闭事件
    this._activeSpeakerObserver.observer.on('close', () => {
      logger.error('activeSpeakerObserver【activeSpeakerObserverId:%s】 的close关闭事件被触发了', activeSpeakerObserver.id);
    });
    // 暂停事件
    this._activeSpeakerObserver.observer.on('pause', () => {
      logger.info('activeSpeakerObserver【activeSpeakerObserverId:%s】 的pause暂停事件被触发了', activeSpeakerObserver.id);
    });
    // 恢复事件
    this._activeSpeakerObserver.observer.on('resume', () => {
      logger.info('activeSpeakerObserver【activeSpeakerObserverId:%s】 的resume恢复事件被触发了', activeSpeakerObserver.id);
    });
    // 添加生产者事件
    this._activeSpeakerObserver.observer.on('addproducer', () => {
      logger.info('activeSpeakerObserver【activeSpeakerObserverId:%s】 的addproducer添加生产者事件被触发了', activeSpeakerObserver.id);
    });
    // 移除生产者事件
    this._activeSpeakerObserver.observer.on('removeproducer', () => {
      logger.info('activeSpeakerObserver【activeSpeakerObserverId:%s】 的removeproducer移除生产者事件被触发了', activeSpeakerObserver.id);
    });
    // 绑定扬声器监听,当一个主导扬声器被检测出时触发，
    this._activeSpeakerObserver.on('dominantspeaker', (dominantSpeaker) => {
      logger.info("activeSpeakerObserver 扬声器检测触发了 【dominantSpeaker:%o】", dominantSpeaker);
    });

    // 绑定音量级别监听
    // 监听路由器的关闭事件
    this._audioLevelObserver.on('routerclose', () => {
      logger.error('audioLevelObserver 【audioLevelObserverId:%s】 监听到 router 【routerId:%s】关闭了,将触发audioLevelObserver的关闭事件', audioLevelObserver.id, router.id);
    });
    // activeSpeakerObserver 的关闭事件
    this._audioLevelObserver.observer.on('close', () => {
      logger.error('audioLevelObserver【audioLevelObserverId:%s】 的close关闭事件被触发了', audioLevelObserver.id);
    });
    // 暂停事件
    this._audioLevelObserver.observer.on('pause', () => {
      logger.info('audioLevelObserver【audioLevelObserverId:%s】 的pause暂停事件被触发了', audioLevelObserver.id);
    });
    // 恢复事件
    this._audioLevelObserver.observer.on('resume', () => {
      logger.info('audioLevelObserver【audioLevelObserverId:%s】 的resume恢复事件被触发了', audioLevelObserver.id);
    });
    // 添加生产者事件
    this._audioLevelObserver.observer.on('addproducer', () => {
      logger.info('audioLevelObserver【audioLevelObserverId:%s】 的addproducer添加生产者事件被触发了', audioLevelObserver.id);
    });
    // 移除生产者事件
    this._audioLevelObserver.observer.on('removeproducer', () => {
      logger.info('audioLevelObserver【audioLevelObserverId:%s】 的removeproducer移除生产者事件被触发了', audioLevelObserver.id);
    });
    // 音量监听
    this._audioLevelObserver.on('volumes', (volumes) => {
      //logger.info("audioLevelObserver 音量级别事件volumes监听触发了 【volumes:%o】", volumes);
      const { producer, volume } = volumes[0];
      // Notify all Peers.
      for (const peer of this._getJoinedPeers({ excludePeerId: undefined })) {
        peer.notify(
          'activeSpeaker', {
          peerId: producer.appData.peerId,
          volume: volume
        }).catch(() => { });
      }
    })
    // 静音监听
    this._audioLevelObserver.on('silence', () => {
      // logger.info("audioLevelObserver 音量级别事件silence监听触发了");
      // Notify all Peers.
      for (const peer of this._getJoinedPeers({ excludePeerId: undefined })) {
        peer.notify('activeSpeaker', { peerId: null })
          .catch(() => { });
      }
    })
  }

  // 关闭函数
  close () {
    logger.error('close() room 【roomId: %s】正在关闭...', this.roomId);
    // 重置room中的成员变量值
    this._closed = true;
    // 调用router 关闭,所对应的transport 以及生产者和消费者都将触发关闭
    this._router.close();
    // 关闭protooRoom
    this._protooRoom.close();
    // 触发room close 事件
    this.emit('close');
  }

  // 日志记录状态
  logStatus () {
    logger.info('当前房间room【roomId:%s】中有【%d】个peer', this._roomId, this._protooRoom.peers.length);
  }

  // 处理连接函数,这边的可以不用同步处理
  handleConnectionRequest ({ webSocketTransport, peerId }) {
    logger.info('正在处理客户端连接...');
    // 根据参数在当前room中创建一个新的peer与客户端对应
    // 判断peerId在该房间内是否存在,如果存在则关闭
    let peer = this._protooRoom.getPeer(peerId);
    if (peer && !peer.closed) {
      peer.close();
    }
    try {
      // 重新创建并初始化peer，peer中的data属性存放peer的成员变量属性
      peer = this._protooRoom.createPeer(peerId, webSocketTransport);
      logger.info('在room 【roomId:%s】中创建一个peer 【peerId:%s】，客户端连接成功', this._roomId, peer.id);
    } catch (error) {
      logger.erorr('在room 【roomId:%s】中创建peer失败，原因:【%o】', this._roomId, error);
    }

    // 定义peer 相关参数
    // peer 的展示的名称
    peer.data.displayName = undefined;
    // peer 对应的设备信息
    peer.data.device = undefined;
    // peer 对应的加入房间的标识
    peer.data.joined = false;
    // peer 对应的客户端的相关参数，通过信令传递过来
    peer.data.rtpCapabilities = undefined;
    peer.data.sctpCapabilities = undefined;

    // 定义peer内的成员变量
    peer.data.transports = new Map();
    // peer 中的生产者，音频/视频生产者；一般有两个分别为音频和视频生产者
    peer.data.producers = new Map();
    // peer 中的消费者，多个消费者，与订阅房间内的其他peer有关
    peer.data.consumers = new Map();

    // 绑定监听事件
    // 监听websocket请求事件;request->response
    peer.on('request', (request, accept, reject) => {
      this._handleProtooRequest({ peer, request, accept, reject }).catch((error) => {
        logger.error('处理protoo request 失败！原因: %o', error);
        // 由于在_handleProtooRequest可能会抛出异常，没有进行reject，这里进行捕获处理
        reject(error);
      });
    });
    // 监听websocket通知事件,不用响应
    peer.on('notification', (notification) => {
      this._handleProtooNotification({ peer, notification }).catch((error) => {
        logger.error('处理protoo notification 失败！原因: %o', error);
        // 由于在_handleProtooRequest可能会抛出异常，没有进行reject，这里进行捕获处理
        reject(error);
      });
    });
    // 监听peer的关闭事件,当protooRoom 或 transport 或peer close() 方法被调用时触发
    peer.on('close', () => {
      logger.error('peer【peerId:%s】 正在关闭...', peer.id);
      // 判断房间是否关闭
      if (this._closed) {
        return;
      }
      if (peer.data.joined) {
        // 通知其他peer
        for (const otherJoinedPeer of this._getJoinedPeers({ excludePeerId: peer.id })) {
          otherJoinedPeer.notify('peerClosed', {
            peerId: peer.id
          }).catch((error) => {
            logger.error('peer 【peerId:%s】关闭通知失败', peer.id);
          });
        }
      }


      // peer 对应的mediasoup transport 关闭,对应的 生产者和消费者都会触发关闭事件
      for (const transport of peer.data.transports.values()) {
        transport.close();
      }

      // 判断是否是房间内的最后一个peer，如果是则关闭房间，不是则通知房间内的其他peer该peer已关闭
      if (this._protooRoom.peers.length === 0) {
        logger.error('由于peer【peerId:%s】是房间【roomId:%s】内最后一个peer，所以peer关闭后该房间也将关闭', peer.id, this._roomId);
        this.close();
      }
    });
  }

  // 处理protoo请求
  async _handleProtooRequest ({ peer, request, accept, reject }) {
    logger.info('正在处理protoo request 【method:%s, data:%o】请求...', request.method, request.data);
    switch (request.method) {
      // 获取服务端的router的rtpCapabilities，供客户端device.load()方法使用
      case 'getRouterRtpCapabilities': {
        logger.info('正在获取router的rtpCapabilities...');
        accept(this._router.rtpCapabilities);
        break;
      }

      case 'createWebRtcTransport': {
        logger.info('正在创建webRtcTransport...');
        // 这里peer.data.joined 为true 不是必须的，可以为false
        // 获取客户端传递过来的参数，producing=true&&consuming=false 表示是生产者传输通道，producing=false&&consuming=true表示是消费者传输通道
        const { producing, consuming, forceTcp, sctpCapabilities } = request.data;
        // 构建webrtc参数
        let webRtcTransportOptions = {
          // 公网ip
          ...config.mediasoup.webRtcTransportOptions,
          enableSctp: Boolean(sctpCapabilities),
          numSctpStreams: (sctpCapabilities || {}).numStreams,
          // 注意：这里后面会使用到，标识该传输通道是生产者还是消费者
          appData: { producing, consuming }
        };
        if (forceTcp) {
          webRtcTransportOptions.enableTcp = true;
          webRtcTransportOptions.enableUdp = false;
        }
        const transport = await this._router.createWebRtcTransport(webRtcTransportOptions);

        // 绑定监听事件
        transport.on('routerclose', () => {
          logger.info('transport 【transportId:%s】监听到router 【routerId:%s】关闭事件', transport.id, this._router.id);
        });
        // 监听trace 事件,这里可以检测带宽
        transport.on('trace', (trace) => {
          logger.info('transport trace 事件触发了，【trace:%o】', trace);
        });
        transport.on('icestatechange', (iceState) => {
          logger.info('transport icestatechange ice 状态改变，事件触发了【iceState:%o】', iceState);
        });
        transport.on('iceselectedtuplechange', (iceSelectedTuple) => {
          logger.info('transport iceselectedtuple 状态改变，事件触发了【iceSelectedTuple:%o】', iceSelectedTuple);

        });
        transport.on('dtlsstatechange', (dtlsState) => {
          logger.info('transport dtlsstatechange 状态改变，事件触发了【dtlsState:%o】', dtlsState);
          if (dtlsState === 'failed' || dtlsState === 'closed') {
            logger.warn('webrtcTransport dtlsstatechange 为失败或关闭了');
          }
        });
        transport.on('sctpstatechange', (sctpState) => {
          logger.info('transport sctpstatechange 状态改变，事件触发了【sctpState:%o】', sctpState);

        });

        // 绑定观察者
        transport.observer.on('close', () => {
          logger.error('transport close 关闭事件触发了，正在关闭transport【transportId:%s】', transport.id);

        });
        transport.observer.on('newproducer', (producer) => {
          logger.info('transport newproducer 有新的生产者【producer:%s】创建', producer.id);

        });
        transport.observer.on('newconsumer', (consumer) => {
          logger.info('transport newconsumer 有新的消费者【consumer:%s】创建', consumer.id);

        });


        // 存储传输通道transport
        peer.data.transports.set(transport.id, transport);
        // 响应客户端传输通道的参数
        accept({
          id: transport.id,
          iceParameters: transport.iceParameters,
          iceCandidates: transport.iceCandidates,
          dtlsParameters: transport.dtlsParameters,
          sctpParameters: transport.sctpParameters,
        });
        // 设置最大帧率
        const { maxIncomingBitrate } = config.mediasoup.webRtcTransportOptions;

        // If set, apply max incoming bitrate limit.
        if (maxIncomingBitrate) {
          try {
            await transport.setMaxIncomingBitrate(maxIncomingBitrate);
          } catch (error) {
            logger.error('transport【transportId:%s】设置最大传入比特率失败，原因:%o', transport.id, error);
          }
        }
        break;
      }


      // 连接webRtcTransport，一般当第一次transport.produce()方法被调用的时候会触发连接传输通道事件
      case 'connectWebRtcTransport': {
        // 这里peer.data.joined 为true不是必须的
        logger.info('正在连接webRtcTransport...');
        // 解析参数
        const { transportId, dtlsParameters } = request.data;
        // 获取transport
        const transport = peer.data.transports.get(transportId);
        // 判断给的transportId是否是合法的
        if (!transport) {
          logger.error('根据【transportId:%s】没有找到对应的transport', transportId);
          throw new Error('根据没有找到对应的transport');
        }
        //连接transport
        await transport.connect({ dtlsParameters });
        // 正常响应
        accept();
        break;
      }



      // 客户端连接成功后,创建mediasoup的transport传输通道都创建好后发送加入房间信令
      case 'join': {
        logger.info('正在加入房间...');
        // 判断peer是否已经加入房间
        if (peer.data.joined) {
          logger.error('peer 【peerId:%s】已经加入该房间中', peer.id);
          throw new Error('peer已经加入该房间中');
        }
        // 解析获取客户端的一些基础信息
        const {
          displayName,
          device,
          rtpCapabilities,
          sctpCapabilities
        } = request.data;
        // 将peer的相关属性进行赋值
        peer.data.joined = true;
        peer.data.displayName = displayName;
        peer.data.device = device;
        peer.data.rtpCapabilities = rtpCapabilities;
        peer.data.sctpCapabilities = sctpCapabilities;
        // 获取该房间内已经加入房间的其她peers
        const otherJoinedPeers = this._getJoinedPeers({ excludePeerId: peer.id });
        // 组装部分信息，如果将整个peer传递过去，数据可能太大，不太合适
        const otherJoinedPeerInfos = otherJoinedPeers.map((otherJoinedPeer) => {
          return {
            id: otherJoinedPeer.id,
            device: otherJoinedPeer.data.device,
            displayName: otherJoinedPeer.data.displayName
          }
        });
        // 将房间内的其她人返回给这个新加入的peer,让其进行处理
        accept({ peers: otherJoinedPeerInfos });
        // 为这个新的peer创建其他端的消费者
        for (const otherJoinedPeer of otherJoinedPeers) {
          // 通知房间内的其他peer有新的peer加入了
          await otherJoinedPeer.request('newPeer', {
            id: peer.id,
            device: peer.data.device,
            displayName: peer.data.displayName
          });
          for (const otherJoinedPeerProducer of otherJoinedPeer.data.producers.values()) {
            logger.info('为新的peer【peerId:%s】订阅该房间内的其他peer【otherPeerId:%s】的 【producer:%o】生产者', peer.id, otherJoinedPeer.id, otherJoinedPeerProducer);
            this._createConsumer({
              // 消费者peer
              consumerPeer: peer,
              // 生产者peer (被消费peer)
              producerPeer: otherJoinedPeer,
              // 生产者 (被消费)
              producer: otherJoinedPeerProducer
            });
          }
          logger.info('protoo 通知房间内其他peer【peerId:%s】有新的peer加入', peer.id);

        }
        break;
      }





      // 为新加入的peer创建生产者  
      case 'produce': {
        logger.info('正在为新加入的peer创建生产者...');
        // 判断peer是否加入房间
        if (!peer.data.joined) {
          logger.error('创建生产者时检测到peer【peerId:%s】还未加入房间', peer.id);
          throw new Error('创建生产者时检测到peer还未加入房间');
        }
        // 解析请求数据
        const { transportId, kind, rtpParameters } = request.data;
        let { appData } = request.data;
        logger.info('客户端传递过来的生产者appData【appData:%o】', appData);
        // 根据transportId 获取transport
        const transport = peer.data.transports.get(transportId);
        // 判断transport是否存在
        if (!transport) {
          logger.error('创建生产者时根据transportId【transportId:%s】没有找到对应的transport', transportId);
          throw new Error('创建生产者时根据transportId没有找到对应的transport');
        }
        // 将peerId 加入进去
        appData = { ...appData, peerId: peer.id };
        // 定义生产参数
        const producerOptions = {
          kind: kind,
          rtpParameters: rtpParameters,
          appData: appData
        };

        let producer;
        try {
          producer = await transport.produce(producerOptions);
          // 存储生产者
          peer.data.producers.set(producer.id, producer);
          // 监听生产者事件
          producer.on('transportclose', () => {
            logger.error('生产者【producerId:%s】监听到其所对应的transport【transportId:%s】关闭事件', producer.id, transportId);
          });
          // 通知生产者的分数更新
          producer.on('score', (score) => {
            logger.info('生产者【producerId:%s】监听到得分事件触发【score:%o】', producer.id, score);
            // 通知客户端生产者的得分
            peer.notify('producerScore', {
              producerId: producer.id,
              score
            });
          });
          producer.on('videoorientationchange', (videoOrientation) => {
            logger.info('生产者【producerId:%s】监听视频方向发生改变【videoOrientation:%o】', producer.id, videoOrientation);
          });
          producer.on('trace', (trace) => {
            logger.info('生产者【producerId:%s】监听到跟踪事件【trace:%o】', producer.id, trace);
          });

          // 生产者观察者事件
          producer.observer.on('close', () => {
            logger.error('生产者【producerId:%s】监听close关闭事件', producer.id);
          });
          producer.observer.on('pause', () => {
            logger.info('生产者【producerId:%s】监听pause暂停事件', producer.id);
          });
          producer.observer.on('resume', () => {
            logger.info('生产者【producerId:%s】监听resume恢复事件', producer.id);
          });

          // 响应给客户端
          accept({ id: producer.id });
          // 为房间内的其他peer创建消费者，该peer作为其他peer的生产者
          for (const otherJoinedPeer of this._getJoinedPeers({ excludePeerId: peer.id })) {
            logger.info('为房间【roomId:%s】内的peer【peerId:%s】创建消费者【consumerPeerId:%s】', this._roomId, otherJoinedPeer.id, peer.id);
            this._createConsumer({
              consumerPeer: otherJoinedPeer,
              producerPeer: peer,
              producer: producer
            });
          }

          // 给房间内的声音监听audioLevelObserver 观察者添加生产者
          if (producer.kind === 'audio') {
            this._audioLevelObserver.addProducer({ producerId: producer.id }).catch((error) => {
              logger.error('为房间【roomId:%s】内的audioLevelObserver 添加生产者失败！原因:%o', this.roomId, error);
            });
          }
        } catch (error) {
          logger.error('创建生产者失败！原因:%o', error);
          reject(error);
        }

        break;
      }


      // 关闭生产者  
      case 'closeProducer': {
        logger.info('正在处理关闭生产者...');
        // 判断peer是否加入房间
        if (!peer.data.joined) {
          logger.error('关闭生产者时检测到peer【peerId:%s】还未加入房间', peer.id);
          throw new Error('关闭生产者时检测到peer还未加入房间');
        }
        // 获取请求信息
        const { producerId } = request.data;
        // 根据producerId 获取producer
        const producer = peer.data.producers.get(producerId);
        // 判断producer是否存在
        if (!producer) {
          logger.error('关闭生产者时根据producerId【producerId:%s】没有找到对应的producer', producerId);
          throw new Error('关闭生产者时根据producerId没有找到对应的producer');
        }
        producer.close();
        // 从存储中移除该生产者
        peer.data.producers.delete(producerId);
        // 响应
        accept();
        break;
      }



      // 暂停生产者  
      case 'pauseProducer': {
        logger.info('正在处理暂停生产者...');
        // 判断peer是否加入房间
        if (!peer.data.joined) {
          logger.error('暂停生产者时检测到peer【peerId:%s】还未加入房间', peer.id);
          throw new Error('暂停生产者时检测到peer还未加入房间');
        }
        // 获取请求信息
        const { producerId } = request.data;
        // 根据producerId 获取producer
        const producer = peer.data.producers.get(producerId);
        // 判断producer是否存在
        if (!producer) {
          logger.error('暂停生产者时根据producerId【producerId:%s】没有找到对应的producer', producerId);
          throw new Error('暂停生产者时根据producerId没有找到对应的producer');

        }
        await producer.pause();
        accept();
        break;
      }


      // 恢复生产者
      case 'resumeProducer': {
        logger.info('正在处理恢复生产者...');
        // 判断peer是否加入房间
        if (!peer.data.joined) {
          logger.error('恢复生产者时检测到peer【peerId:%s】还未加入房间', peer.id);
          throw new Error('恢复生产者时检测到peer还未加入房间');
        }
        // 获取请求信息
        const { producerId } = request.data;
        // 根据producerId 获取producer
        const producer = peer.data.producers.get(producerId);
        // 判断producer是否存在
        if (!producer) {
          logger.error('恢复生产者时根据producerId【producerId:%s】没有找到对应的producer', producerId);
          throw new Error('恢复生产者时根据producerId没有找到对应的producer');
        }
        await producer.resume();
        accept();
        break;
      }


      // 暂停消费者 
      case 'pauseConsumer': {
        logger.info('正在处理暂停消费者...');
        // 判断peer是否加入房间
        if (!peer.data.joined) {
          logger.error('暂停消费者时检测到peer【peerId:%s】还未加入房间', peer.id);
          throw new Error('暂停消费者时检测到peer还未加入房间');
        }
        // 获取请求信息
        const { consumerId } = request.data;
        // 根据consumerId 获取consumer
        const consumer = peer.data.consumers.get(consumerId);
        // 判断consumer是否存在
        if (!consumer) {
          logger.error('暂停消费者时根据consumerId【consumerId:%s】没有找到对应的consumer', consumerId);
          throw new Error('暂停消费者时根据consumerId没有找到对应的consumer');
        }
        await consumer.pause();
        accept();
        break;
      }


      // 恢复消费者
      case 'resumeConsumer': {
        logger.info('正在处理恢复消费者...');
        // 判断peer是否加入房间
        if (!peer.data.joined) {
          logger.error('恢复消费者时检测到peer【peerId:%s】还未加入房间', peer.id);
          throw new Error('恢复消费者时检测到peer还未加入房间');
        }
        // 获取请求信息
        const { consumerId } = request.data;
        // 根据consumerId 获取consumer
        const consumer = peer.data.consumers.get(consumerId);
        // 判断consumer是否存在
        if (!consumer) {
          logger.error('恢复消费者时根据consumerId【consumerId:%s】没有找到对应的consumer', consumerId);
          throw new Error('恢复消费者时根据consumerId没有找到对应的consumer');
        }
        await consumer.resume();
        accept();
        break;
      }

      // 重启ice
      case 'restartIce': {
        logger.info('正在重启ice...');
        // 这里的peer是否加入没有多大的关系
        // 获取传递过来的参数
        const { transportId } = request.data;
        // 获取transport
        const transport = peer.data.transports.get(transportId);
        // 判断transport是否存在
        if (!transport) {
          logger.error('重启ice时根据transportId【transportId:%s】没有找到对应的transport', transportId);
          throw new Error('重启ice时根据没有找到对应的transport');
        }
        try {
          const iceParameters = await transport.restartIce();
          accept({ iceParameters });
        } catch (error) {
          logger.error('重启ice失败！');
          reject(error);
        }

        break;
      }

      // 设置消费者首选层  
      case 'setConsumerPreferredLayers': {
        logger.info('正在处理设置消费者首选层...');
        // 判断peer是否加入房间
        if (!peer.data.joined) {
          logger.error('处理设置消费者首选层时检测到peer【peerId:%s】还未加入房间', peer.id);
          throw new Error('处理设置消费者首选层时检测到peer还未加入房间');
        }
        // 获取请求信息
        const { consumerId, spatialLayer, temporalLayer } = request.data;
        // 根据consumerId 获取consumer
        const consumer = peer.data.consumers.get(consumerId);
        // 判断consumer是否存在
        if (!consumer) {
          logger.error('处理设置消费者首选层时根据consumerId【consumerId:%s】没有找到对应的consumer', consumerId);
          throw new Error('处理设置消费者首选层者时根据consumerId没有找到对应的consumer');
        }
        await consumer.setPreferredLayers({ spatialLayer, temporalLayer });
        accept();
        break;
      }


      // 请求消费者关键帧
      case 'requestConsumerKeyFrame': {
        logger.info('正在处理请求消费者关键帧...');
        // 判断peer是否加入房间
        if (!peer.data.joined) {
          logger.error('请求消费者关键帧时检测到peer【peerId:%s】还未加入房间', peer.id);
          throw new Error('请求消费者关键帧时检测到peer还未加入房间');
        }
        // 获取请求信息
        const { consumerId } = request.data;
        // 根据consumerId 获取consumer
        const consumer = peer.data.consumers.get(consumerId);
        // 判断consumer是否存在
        if (!consumer) {
          logger.error('请求消费者关键帧时根据consumerId【consumerId:%s】没有找到对应的consumer', consumerId);
          throw new Error('请求消费者关键帧时根据consumerId没有找到对应的consumer');
        }
        await consumer.requestKeyFrame();
        accept();
        break;
      }


      // 设置消费者优先级
      case 'setConsumerPriority': {
        logger.info('正在处理设置消费者优先级...');
        // 判断peer是否加入房间
        if (!peer.data.joined) {
          logger.error('设置消费者优先级时检测到peer【peerId:%s】还未加入房间', peer.id);
          throw new Error('设置消费者优先级时检测到peer还未加入房间');
        }
        // 获取请求信息
        const { consumerId, priority } = request.data;
        // 根据consumerId 获取consumer
        const consumer = peer.data.consumers.get(consumerId);
        // 判断consumer是否存在
        if (!consumer) {
          logger.error('设置消费者优先级时根据consumerId【consumerId:%s】没有找到对应的consumer', consumerId);
          throw new Error('设置消费者优先级时根据consumerId没有找到对应的consumer');
        }
        await consumer.setPriority(priority);
        accept();
        break;
      }


      // 前端设置改变展示的名称  
      case 'changeDisplayName': {
        logger.info('正在处理设置改变展示的名称...');
        // 判断peer是否加入房间
        if (!peer.data.joined) {
          logger.error('设置改变展示的名称时检测到peer【peerId:%s】还未加入房间', peer.id);
          throw new Error('设置改变展示的名称时检测到peer还未加入房间');
        }
        const { displayName } = request.data;
        // 老的展示名称
        const oldDisplayName = peer.data.displayName;
        // 替换存储起来
        peer.data.displayName = newDisplayName;
        // 通知房间内的其他peer该名称已经改变
        for (const otherJoinedPeer of this._getJoinedPeers({ excludePeerId: peer.id })) {
          otherJoinedPeer.notify('peerDisplayNameChanged', {
            peerId: peer.id,
            displayName,
            oldDisplayName: oldDisplayName
          }).catch((error) => {
            logger.error('通知peer的展示名称失败！原因:%o', error);
          });
        }
        accept();
        break;
      }


      // 获取transport传输通道的统计数据  
      case 'getTransportStats': {
        // 获取请求的数据
        const { transportId } = request.data;
        // 根据transportId 获取transport
        const transport = peer.data.transports.get(transportId);
        // 判断transport是否存在
        if (!transport) {
          logger.error('获取transport传输通道的统计数据时根据transportId【transportId:%s】没有找到对应的transport', transportId);
          throw new Error('获取transport传输通道的统计数据时根据transportId没有找到对应的transport');
        }
        const stats = await transport.getStats();
        accept(stats);
        break;
      }


      // 获取生产者的统计信息
      case 'getProducerStats': {
        // 获取请求信息
        const { producerId } = request.data;
        // 根据producerId 获取producer
        const producer = peer.data.producers.get(producerId);
        // 判断producer是否存在
        if (!producer) {
          logger.error('获取生产者的统计信息时根据producerId【producerId:%s】没有找到对应的producer', producerId);
          throw new Error('获取生产者的统计信息时根据producerId没有找到对应的producer');
        }
        const stats = await producer.getStats();
        accept(stats);
        break;
      }
      // 获取消费者统计数据  
      case 'getConsumerStats': {
        // 获取请求信息
        const { consumerId } = request.data;
        // 根据consumerId 获取consumer
        const consumer = peer.data.consumers.get(consumerId);
        // 判断consumer是否存在
        if (!consumer) {
          logger.error('获取消费者统计数据时根据consumerId【consumerId:%s】没有找到对应的consumer', consumerId);
          throw new Error('获取消费者统计数据时根据consumerId没有找到对应的consumer');
        }
        const stats = await consumer.getStats();
        accept(stats);
        break;
      }
      default: {
        logger.error('protoo request 没有匹配到符合的方法【method:%s】', request.method);
        reject(400, `protoo request 没有匹配到符合的方法 【method:"${request.method}"】`);
        break;
      }

    }
  }

  // 处理protoo通知
  async _handleProtooNotification ({ peer, notification }) {
    logger.info('正在处理protoo notification 【method:%s, data:%o】请求...', notification.method, notification.data);
    switch (notification.method) {
      case '':
        break;
      case '':
        break;
      default:
        break;
    }
  }

  //  返回已经已经加入的该房间内的所有peer
  _getJoinedPeers ({ excludePeerId }) {
    const peers = this._protooRoom.peers.filter((peer) => {
      if (excludePeerId) {
        return peer.data.joined && peer.id != excludePeerId;
      }
      return peer.data.joined;
    });
    logger.info('获取当前已经加入room中的所有符合【excludePeerId:%s】条件的peer【%d】个', excludePeerId, peers.length);
    return peers;
  }

  // 创建消费者,生产者是被消费方
  async _createConsumer ({ consumerPeer, producerPeer, producer }) {
    logger.info('正在为【peerId:%s】创建消费者...', consumerPeer.id);
    // 判断cnsumerPeer 中的rtpCapabilities 是否是合法的
    const consumerRtpCapabilities = consumerPeer.data.rtpCapabilities;
    if (!consumerRtpCapabilities || !this._router.canConsume({ producerId: producer.id, rtpCapabilities: consumerRtpCapabilities })) {
      logger.error('非法的消费者 rtpCapabilities !');
      return;
    }
    // 获取消费者transport
    const transport = Array.from(consumerPeer.data.transports.values())
      .find((t) => t.appData.consuming && !t.appData.producing);
    // 判断transport 是否存在
    if (!transport) {
      logger.error('消费者peer中没有找到消费transport');
      return;
    }
    // 构建消费者参数
    const consumerOptions = {
      producerId: producer.id,
      rtpCapabilities: consumerRtpCapabilities,
      paused: true,
    };
    // 创建消费者
    let consumer;
    try {
      consumer = await transport.consume(consumerOptions);
    } catch (error) {
      logger.error('transport【transportId:%s】创建消费者失败', transport.id);
      return;
    }

    // 存储到消费者peer中
    consumerPeer.data.consumers.set(consumer.id, consumer);
    // 绑定消费者监听事件
    consumer.on('transportclose', () => {
      logger.error('消费者consumer 【consumerId:%s】监听到传输通道transport【transportId:%s】 close 关闭事件触发了', consumer.id, transport.id);
      //移除消费者从消费者peer的data中
      consumerPeer.data.consumers.delete(consumer.id);
    });
    consumer.on('producerclose', () => {
      logger.error('消费者consumer 【consumerId:%s】监听到关联的生产者producer【producerId:%s】 close 关闭事件触发了', consumer.id, producer.id);
      // 如果消费者关联的生产者关闭，也将移除,并通知消费者peer关闭客户端消费者并进行处理
      consumerPeer.data.consumers.delete(consumer.id);
      consumerPeer.notify('consumerClosed', {
        consumerId: consumer.id
      }).catch(() => {

      });
    });
    consumer.on('producerpause', () => {
      logger.info('消费者consumer 【consumerId:%s】监听到关联的生产者producer【producerId:%s】 pause 暂停事件触发了', consumer.id, producer.id);
      //如果生产者暂停，通知客户端消费者暂停、
      consumerPeer.notify('consumerPaused', {
        consumerId: consumer.id
      }).catch(() => {

      });
    });
    consumer.on('producerresume', () => {
      logger.info('消费者consumer 【consumerId:%s】监听到关联的生产者producer【producerId:%s】 resume 恢复事件触发了', consumer.id, producer.id);
      consumerPeer.notify('consumerResumed', {
        consumerId: consumer.id
      }).catch(() => {

      });
    });
    consumer.on('score', (score) => {
      logger.info('消费者consumer 【consumerId:%s】监听到消费者分数改变【score:%o】', consumer.id, score);
      consumerPeer.notify('consumerScore', {
        consumerId: consumer.id, score
      }).catch(() => { });
    });
    consumer.on('layerschange', (layers) => {
      logger.info('同播模式下消费者consumer 【consumerId:%s】监听到时间层或空间层改变【layers:%o】', consumer.id, layers);
      consumerPeer.notify(
        'consumerLayersChanged',
        {
          consumerId: consumer.id,
          spatialLayer: layers ? layers.spatialLayer : null,
          temporalLayer: layers ? layers.temporalLayer : null
        })
        .catch(() => { });
    });
    consumer.on('trace', (trace) => {
      logger.info('消费者consumer 【consumerId:%s】监听到跟踪事件【trace:%o】', consumer.id, trace);
    });


    // 监听观察者
    consumer.observer.on('close', () => {
      logger.info('消费者consumer 【consumerId:%s】监听到关闭事件', consumer.id);
    });
    consumer.observer.on('pause', () => {
      logger.info('消费者consumer 【consumerId:%s】监听到暂停事件', consumer.id);
    });
    consumer.observer.on('resume', () => {
      logger.info('消费者consumer 【consumerId:%s】监听到恢复事件', consumer.id);
    });
    // 通知消费者客户端有新的消费者被创建
    try {
      await consumerPeer.request('newConsumer', {
        peerId: producerPeer.id,
        id: consumer.id,
        producerId: producer.id,
        kind: consumer.kind,
        rtpParameters: consumer.rtpParameters,
        type: consumer.type,
        producerPaused: consumer.producerPaused,
        appData: producer.appData
      });
      // appData 中应该包含peerId 字段
      logger.info('生成者中的producer.appData【appData:%o】', producer.appData);
      // 恢复暂停模式
      await consumer.resume();
      // 通知消费者端消费者分数
      consumerPeer.notify('consumerScore', {
        consumerId: consumer.id,
        score: consumer.score
      }).catch((error) => {
        logger.error('通知消费者peer【peerId:%s】的consumerScore失败！原因:%o', consumerPeer.id, error);
      });
    } catch (error) {
      logger.error('发送消息给消费者peer【peerId:%s】失败！，原因:%o', consumerPeer.id, error);
    }
  }

}

module.exports = Room;
